require 'cucumber/formatter/progress'
require 'cucumber/step_definition_light'
require "json"
require 'cfgtools.rb'

$json_to_file = Integer(get_config_value("json_to_file", 1))
$report_path = get_config_value("report_path", "report")

module RuSalad
  module Formatter
    class JSON
      def initialize(step_mother, io, options)
        @io = io
        @indent = 0
      end

      def respond_to?(*args)
        true
      end

      def escape_json(s)
        x = JSON "aaa" => s
        return x[7..x.length-2]
      end

      def before_feature(feature)
        safename = String(feature.file).gsub(/.*[\\\/]/, "")
        safename = safename.gsub(/\..*/, "");
        @feature_path = File.join($report_path, safename)
        @jsonfile = File.join(@feature_path, "report.json")

        FileUtils.mkpath(@feature_path) unless File.exists?(@feature_path)
        FileUtils.rm_f(@jsonfile) if File.exists?(@jsonfile)

        @background_items = []
        @background_step_count = 0
        @in_background = false
        
        @feature_start_time = Time.now.to_f
        @is_first_scenario = true
        out "{\n"
        out "    featureName: #{escape_json(feature.name)},\n"
        out "    safeName: #{escape_json(safename)},\n"
        out "    scenarios: [\n"
      end

      def before_background(background)
        @in_background = true

        @scenario_start_time = Time.now.to_f
        @is_first_step = true
        @scenario_passed = true
        @fe_status = :passed
        @step_no = 0
      end

      def before_feature_element(feature_element)
        @scenario_start_time = Time.now.to_f unless @in_background
        @is_first_step = true unless @in_background
        @scenario_passed = true unless @in_background
        @fe_status = :passed unless @in_background
        @step_no = 1
        @is_within_first_scenario = false
        if (@is_first_scenario) then
          @is_first_scenario = false
          @is_within_first_scenario = true
          out "        {\n"
        else
          out "       ,{\n"
        end
        out "            scenarioName: #{escape_json feature_element.name},\n"
        out "            steps: [\n"
        if @in_background
          @in_background = false
          @background_items.each { |item|
            out item
          }
          @background_items = []
        end
      end

      def before_step(step)
        @step_start_time = Time.now.to_f
        @step_start_time_relative = @step_start_time - @feature_start_time

        outstr = ""
        if (@is_first_step) then
          @is_first_step = false
          outstr = "                {\n"
        else
          outstr = "               ,{\n"
        end
        if @in_background
          @background_items << outstr
          @background_step_count = @background_step_count + 1
        else
          out outstr unless @is_within_first_scenario && @background_step_count >= @step_no
        end
      end

      def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_column_line)
        after_step_result_ext keyword, step_match, multiline_arg, status, exception, source_indent, background, file_column_line, nil
      end

      def after_step_result_ext(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_column_line, screenshot)
        step_duration = Time.now.to_f - @step_start_time
        args = step_match.format_args("<arg>%s</arg>")
        outstr = "                    status: #{escape_json status},\n" +
                 "                    keyword: #{escape_json keyword},\n" +
                 "                    args: #{escape_json args},\n" +
                 "                    exception: #{escape_json exception},\n" +
                 "                    source_indent: #{escape_json source_indent},\n" +
                 "                    start_time: #{@step_start_time_relative},\n" +
                 "                    is_background_step: #{@background_step_count >= @step_no},\n" +
                 "                    screenshot: #{escape_json screenshot},\n" +
                 "                    duration: #{step_duration}\n" +
                 "                }\n"

        if @in_background
          @background_items << outstr
        else
          out outstr unless @is_within_first_scenario && @background_step_count >= @step_no
        end
        @fe_status = :failed if @fe_status == :passed && status == :failed
        @fe_status = :undefined if status == :undefined
        @step_no = @step_no + 1
      end

      def after_feature_element(feature_element) 
        after_feature_element_ext feature_element, "", nil
      end

      def after_feature_element_ext(feature_element, vf, error)
        scenario_duration = Time.now.to_f - @scenario_start_time
        out "            ],\n"
        out "            passed: #{!feature_element.failed?},\n"
        out "            status: #{escape_json @fe_status},\n"
        out "            start_time: #{@scenario_start_time - @feature_start_time},\n"
        out "            duration: #{scenario_duration},\n"
        out "            videoFile: '#{vf}'\n" unless error
        out "            warning: #{escape_json "VIDEO ENCRYPTION ERROR: #{error.message}"}\n" if error
        out "        }"
      end

      def after_feature(feature)
        duration = Time.now.to_f - @feature_start_time
        out "    ],\n"
        out "    duration: #{duration}\n"
        out "}\n"
      end

      def method_missing(name, *args)
      end

      private

      def format_time(time)
        hours = "%02d" % ((time / 3600).to_i)
        minutes = "%02d" % ((time / 60).to_i % 60)
        seconds = "%02d" % (time.to_i % 60)
        milliseconds = "%03d" % (((time - time.to_i) * 1000).to_i)
        return "#{hours}:#{minutes}:#{seconds},#{milliseconds}"
      end

      def indent
        (' ' * @indent)
      end

      def out(s)
        if $json_to_file != 0 and @jsonfile != nil
          json_file = File.exists?(@jsonfile) ? File.new(@jsonfile, 'a') : File.new(@jsonfile, 'w')
          json_file.puts(s)
          json_file.close
        end
        @io.puts s
        @io.flush
      end
    end
  end
end
